//
//  HHPHTTPRequest.m
//  HHPHTTPRequest
//
//  Created by 韩红坡 on 2016/12/13.
//  Copyright © 2016年 韩红坡. All rights reserved.
//

#import "HHPHTTPRequest.h"

@interface HHPHTTPRequest ()

@property (nonatomic, strong) AFHTTPSessionManager *sessionManager;
@property (nonatomic, strong) HHPHTTPRequestOptions *options;

@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *mutableParams;
@property (nonatomic, strong) NSMutableDictionary<NSString *, NSString *> *mutableHeaders;
@property (nonatomic, strong) void (^constructingBodyBlock)(id <AFMultipartFormData> formData);
@property (nonatomic, strong) void (^progressBlock)(HHPHTTPRequest *request, NSProgress *progress);
@property (nonatomic, strong) void (^successBlock)(HHPHTTPRequest *request);
@property (nonatomic, strong) void (^errorBlock)(HHPHTTPRequest *request);

@property (nonatomic, strong) NSURLSessionTask *task;
@property (nonatomic, strong) id data;
@property (nonatomic, strong) NSError *error;

@property (nonatomic, assign) NSInteger retryTimes;
@end

@implementation HHPHTTPRequest

- (instancetype)initWithSessionManager:(AFHTTPSessionManager *)sessionManager
{
    self = [super init];
    if (self) {
        self.sessionManager = sessionManager;
        self.options = [[HHPHTTPRequestOptions alloc] init];
        self.method = HHPHTTPMethodGet;
        self.mutableParams = [NSMutableDictionary dictionary];
        self.mutableHeaders = [NSMutableDictionary dictionary];
    }
    
    return self;
}

- (NSDictionary<NSString *,id> *)params
{
    return [self.mutableParams copy];
}

- (NSDictionary<NSString *,NSString *> *)headers
{
    return [self.mutableHeaders copy];
}

- (void)setParam:(NSString *)name value:(id)value
{
    self.mutableParams[name] = value;
}

- (void)setParams:(NSDictionary<NSString *, id> *)params
{
    self.mutableParams = [params mutableCopy];
}

- (void)setHeader:(NSString *)name value:(NSString *)value
{
    self.mutableHeaders[name] = value;
}

- (void)setHeaders:(NSDictionary<NSString *, NSString *> *)headers
{
    self.mutableHeaders = [headers mutableCopy];
}

- (void)startWithSuccess:(void (^)(HHPHTTPRequest *))successBlock error:(void (^)(HHPHTTPRequest *))errorBlock
{
    [self startWithProgress:nil success:successBlock error:errorBlock];
}

- (void)startWithProgress:(void (^)(HHPHTTPRequest *, NSProgress *))progressBlock success:(void (^)(HHPHTTPRequest *))successBlock error:(void (^)(HHPHTTPRequest *))errorBlock
{
    if (!self.task) {
        self.progressBlock = progressBlock;
        self.successBlock = successBlock;
        self.errorBlock = errorBlock;
        
        AFHTTPRequestSerializer *requestSerializer;
        if (self.options.requestBodyType == HHPHTTPRequestBodyTypeHTTP) {
            requestSerializer = [AFHTTPRequestSerializer serializer];
        } else {
            requestSerializer = [AFJSONRequestSerializer serializer];
        }
        
        AFHTTPResponseSerializer *responseSerializer;
        if (self.options.responseBodyType == HHPHTTPRequestBodyTypeHTTP) {
            responseSerializer = [AFHTTPResponseSerializer serializer];
        } else {
            responseSerializer = [AFJSONResponseSerializer serializer];
        }
        
        if (self.options.defaultHeaders) {
            for (NSString *key in self.options.defaultHeaders) {
                [requestSerializer setValue:self.options.defaultHeaders[key] forHTTPHeaderField:key];
            }
        }
        
        for (NSString *key in self.mutableHeaders) {
            [requestSerializer setValue:self.mutableHeaders[key] forHTTPHeaderField:key];
        }
        
        if (self.options.encryptHandler) {
            self.mutableParams = [self.options.encryptHandler handleEncryptRequestParams:self.mutableParams];
        }
        
        if (self.options.timeout > 0) {
            requestSerializer.timeoutInterval = self.options.timeout;
        }
        
        if (self.options.acceptContentTypes) {
            responseSerializer.acceptableContentTypes = self.options.acceptContentTypes;
        }
        
        if (self.options.securityPolicy) {
            self.sessionManager.securityPolicy = self.options.securityPolicy;
        } else {
            self.sessionManager.securityPolicy = [AFSecurityPolicy defaultPolicy];
        }
        
        requestSerializer.cachePolicy = self.options.cachePolicy;
        
        self.sessionManager.requestSerializer = requestSerializer;
        self.sessionManager.responseSerializer = responseSerializer;
        
        self.task = [self generateTask];
    }
}

- (void)cancel {
    [self.task cancel];
}

- (NSURLSessionDataTask *)generateTask
{
    NSURLSessionDataTask *task;
    void (^resultBlock)(NSURLSessionDataTask *task, id result) = [self createResultBlock];
    
    if (self.method == HHPHTTPMethodGet) {
        task = [self.sessionManager GET:self.url parameters:self.mutableParams progress:^(NSProgress * _Nonnull downloadProgress) {
            if (self.progressBlock) {
                self.progressBlock(self, downloadProgress);
            }
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            resultBlock(task, responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            resultBlock(task, error);
        }];
        
    } else if (self.method == HHPHTTPMethodPost) {
        if (self.constructingBodyBlock) {
            task = [self.sessionManager  POST:self.url parameters:self.mutableParams constructingBodyWithBlock:self.constructingBodyBlock progress:^(NSProgress * _Nonnull uploadProgress) {
                if (self.progressBlock) {
                    self.progressBlock(self, uploadProgress);
                }
            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                resultBlock(task, responseObject);
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                resultBlock(task, error);
            }];
        } else {
            task = [self.sessionManager POST:self.url parameters:self.mutableParams progress:^(NSProgress * _Nonnull uploadProgress) {
                if (self.progressBlock) {
                    self.progressBlock(self, uploadProgress);
                }
            } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                resultBlock(task, responseObject);
            } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                resultBlock(task, error);
            }];
        }
    } else {
        @throw [NSException exceptionWithName:HHPHTTPRequestException reason:[NSString stringWithFormat:@"此http方法(%ld)不被支持", (long)self.method] userInfo:nil];
    }
    
    return task;
}

- (void (^)(NSURLSessionDataTask *task, id result))createResultBlock
{
    //保证request销毁后，回调还能正常调用
    void (^successBlock)(HHPHTTPRequest *) = [self.successBlock copy];
    void (^errorBlock)(HHPHTTPRequest *) = [self.errorBlock copy];
    
    void (^resultBlock)(NSURLSessionDataTask *task, id result) = ^(NSURLSessionDataTask *task, id result) {
        //处理返回的数据
        if (![result isKindOfClass:[NSError class]] && self.options.dataHandler) {
            result = [self.options.dataHandler handleData:result task:task];
        }
        
        if ([result isKindOfClass:[NSError class]] && self.options.errorHandler) {
            result = [self.options.errorHandler handleError:result task:task];
        }
        
        if ([result isKindOfClass:[NSError class]]) {
            self.error = result;
            if (self.options.retryBlock && self.options.retryBlock(self) && self.retryTimes <= 0) {
                self.retryTimes += 1;
                self.task = nil;
                
                [self startWithSuccess:self.successBlock error:self.errorBlock];
            } else {
                if (errorBlock) {
                    errorBlock(self);
                }
                
                // 回调block可能会持有一些对象，主动释放这些block，保证被闭包持有的对象被及时释放
                // 这样可以避免发生循环引用
                self.progressBlock = nil;
                self.successBlock = nil;
                self.errorBlock = nil;
            }
            
        } else {
            self.data = result;
            if (successBlock) {
                successBlock(self);
            }
            
            // 回调block可能会持有一些对象，主动释放这些block，保证被闭包持有的对象被及时释放
            // 这样可以避免发生循环引用
            self.progressBlock = nil;
            self.successBlock = nil;
            self.errorBlock = nil;
        }
    };
    return resultBlock;
}

@end
